#include "Task.hpp"
#include <string>
#include <cstdlib>
#include <cassert>

#include <ctime>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/stat.h>


const int processnum = 5;
std::vector<task_t> tasks;

//先描述
class channel
{
public:
    channel(int cmdfd,int slaverid,const std::string &processname)
        :_cmdfd(cmdfd)
        ,_salverid(slaverid)
        ,_processname(processname)
    {}
public:
    int _cmdfd;                  //发送任务的文件描述符
    pid_t _salverid;             //子进程的PID
    std::string _processname;    //子进程的名字 -- 方便打印日志
};

//extern std::vector<task_t> tasks;

void slaver()
{
    while (true)
    {
        int cmdcode = 0;
        int n = read(0,&cmdcode,sizeof(int));//如果父进程不给子进程发送任务,阻塞等待.

        if (n == sizeof(int))
        {
            //执行cmdcode对应的任务列表
            std::cout << "salver say@ get a command: " << getpid() << ", cmdcode:  " << cmdcode << std::endl;

            if (cmdcode >= 0 && cmdcode < tasks.size())
                tasks[cmdcode]();
        }

        if (n == 0)
        {
            break;
        }


    }
    
}

//参数规范
//输入: const &
//输出: *
//输入输出: &

void InitProcessPool(std::vector<channel> *channels)
{
    std::vector<int> oldfds;//确保每一个子进程只有一个写端

    for (int i = 0; i < processnum; i++)
    {

        int pipefd[2] = {0};
        int n = pipe(pipefd);

        assert(!n);
        (void)n;

        

        pid_t id = fork();

        if (id == 0)
        {
            std::cout << "child: " << getpid() << " close history fd: ";
            for (auto fd : oldfds)
            {
                std::cout << fd << " ";
                close(fd);
            }
            std::cout << std::endl;
            //child
            close(pipefd[1]);
            dup2(pipefd[0],0);
            slaver();

            std::cout << "process : " << getpid() << "quit" << std::endl;
            exit(0);
        }
        //father
        close(pipefd[0]);

        //添加channel字段
        std::string name = "process-" + std::to_string(i);
        channels->push_back(channel(pipefd[1],id,name));

        oldfds.push_back(pipefd[1]);

       // sleep(1);
    }
}

void Debug(std::vector<channel>& channels)
{
    for (auto &c : channels)
    {
        std::cout  <<  c._cmdfd << " " << c._processname << " " << c._salverid << std::endl;  
    }
}

void Menu()
{
    std::cout << "#############################################" << std::endl;
    std::cout << "##    1. 刷新日志         2. 更新野区        ##" << std::endl;
    std::cout << "##    3. 检测更新         4. 释放技能        ##" << std::endl;
    std::cout << "##                0. 退出                   ##" << std::endl;
    std::cout << "#############################################" << std::endl;

}

void CtrlSlaver(const std::vector<channel>& channels)
{
    int which = 0;

     //2. 开始控制子进程
    //int cnt = 5;
    while (true)
    {
        int select = 0;
        Menu();
        std::cout << "Please Enter@ ";
        std::cin >> select;

        if (select <= 0 || select >= 5) break;

        int cmdcode = select - 1; 

        // 1. 选择任务
        //int cmdcode = rand() % tasks.size();
        // 2. 选择进程
        //int processpos = rand() % channels.size();

        std::cout << "father say@ " << "comcode: " << cmdcode << ", already sendto " << channels[which]._salverid << ", process name: "
        << channels[which]._processname << std::endl;
        // 3. 发送任务
        write(channels[which]._cmdfd,&cmdcode,sizeof(cmdcode));

        which++;
        which %= channels.size();

        //cnt--;
        sleep(1);
        
    }
}

void QuitProcess(const std::vector<channel>& channels)
{
    for (const auto& c: channels)
    {
        close(c._cmdfd);
    }

    //sleep(10);

    for (const auto& c: channels)
    {
        waitpid(c._salverid,nullptr,0);
    }

    //sleep(5);
}

int main()
{
    srand(time(nullptr)^getpid());

   
    LoadTask(&tasks);

    //在组织
    std::vector<channel> channels;
    //1. 初始化
    InitProcessPool(&channels);
   
    //test
   // Debug(channels);

    CtrlSlaver(channels);

    //3. 清理收尾
    QuitProcess(channels);
}